"""
数据库引擎 V2.0
完全符合实践要求：执行引擎、存储引擎、系统目录集成
"""

import struct
from typing import Dict, List, Optional, Any, Tuple
from storage.page_storage_v2 import PageStorageSystem, TablePageManager
from storage.cache_management_v2 import CacheManager
from storage.system_catalog_v2 import SystemCatalog
from compiler.execution_plan import ExecutionPlan

class Record:
    """记录类"""
    
    def __init__(self, data: Dict[str, Any], schema: Dict[str, Any]):
        """初始化记录"""
        self.data = data
        self.schema = schema
        self.record_id = None  # 记录ID（页面内偏移）
    
    def serialize(self) -> bytearray:
        """序列化记录为字节数组"""
        data_bytes = bytearray()
        
        for col_name, col_def in self.schema['columns'].items():
            value = self.data.get(col_name)
            
            if value is None:
                # NULL值用特殊标记表示
                data_bytes.extend(b'\xFF\xFF\xFF\xFF')
            else:
                data_type = col_def['type']
                if data_type['type'] == 'int':
                    data_bytes.extend(struct.pack('<i', int(value)))
                elif data_type['type'] == 'float':
                    data_bytes.extend(struct.pack('<f', float(value)))
                elif data_type['type'] == 'boolean':
                    data_bytes.extend(struct.pack('<?', bool(value)))
                elif data_type['type'] == 'varchar':
                    # 变长字符串：长度(4字节) + 数据
                    str_value = str(value)
                    str_bytes = str_value.encode('utf-8')
                    data_bytes.extend(struct.pack('<I', len(str_bytes)))
                    data_bytes.extend(str_bytes)
        
        return data_bytes
    
    @classmethod
    def deserialize(cls, data: bytearray, schema: Dict[str, Any], offset: int = 0) -> Tuple['Record', int]:
        """从字节数组反序列化记录"""
        record_data = {}
        pos = offset
        
        for col_name, col_def in schema['columns'].items():
            data_type = col_def['type']
            
            if data_type['type'] == 'int':
                value = struct.unpack('<i', data[pos:pos+4])[0]
                pos += 4
            elif data_type['type'] == 'float':
                value = struct.unpack('<f', data[pos:pos+4])[0]
                pos += 4
            elif data_type['type'] == 'boolean':
                value = struct.unpack('<?', data[pos:pos+1])[0]
                pos += 1
            elif data_type['type'] == 'varchar':
                # 检查是否为NULL
                if data[pos:pos+4] == b'\xFF\xFF\xFF\xFF':
                    value = None
                    pos += 4
                else:
                    length = struct.unpack('<I', data[pos:pos+4])[0]
                    pos += 4
                    value = data[pos:pos+length].decode('utf-8')
                    pos += length
            
            record_data[col_name] = value
        
        record = cls(record_data, schema)
        return record, pos

class ExecutionEngine:
    """执行引擎"""
    
    def __init__(self, storage_engine):
        """初始化执行引擎"""
        self.storage_engine = storage_engine
    
    def execute_plan(self, plan: ExecutionPlan) -> Any:
        """执行执行计划"""
        if plan.plan_type == "CreateTable":
            return self.execute_create_table(plan)
        elif plan.plan_type == "Insert":
            return self.execute_insert(plan)
        elif plan.plan_type == "SeqScan":
            return self.execute_seq_scan(plan)
        elif plan.plan_type == "Filter":
            return self.execute_filter(plan)
        elif plan.plan_type == "Project":
            return self.execute_project(plan)
        elif plan.plan_type == "Update":
            return self.execute_update(plan)
        elif plan.plan_type == "Delete":
            return self.execute_delete(plan)
        else:
            raise Exception(f"不支持的执行计划类型: {plan.plan_type}")
    
    def execute_create_table(self, plan: ExecutionPlan) -> str:
        """执行CreateTable计划"""
        table_name = plan.attributes['table_name']
        schema = plan.attributes['schema']
        
        success = self.storage_engine.create_table(table_name, schema)
        if success:
            return f"表 '{table_name}' 创建成功"
        else:
            raise Exception(f"创建表 '{table_name}' 失败")
    
    def execute_insert(self, plan: ExecutionPlan) -> str:
        """执行Insert计划"""
        table_name = plan.attributes['table_name']
        values = plan.attributes['values']
        columns = plan.attributes['columns']
        
        # 构建记录数据
        record_data = {}
        for col_name, value in zip(columns, values):
            record_data[col_name] = value
        
        # 获取表结构
        schema = self.storage_engine.get_table_schema(table_name)
        if not schema:
            raise Exception(f"表 '{table_name}' 不存在")
        
        # 创建记录
        record = Record(record_data, schema)
        
        # 插入记录
        success = self.storage_engine.insert_record(table_name, record)
        if success:
            return f"成功插入1条记录到表 '{table_name}'"
        else:
            raise Exception(f"插入记录到表 '{table_name}' 失败")
    
    def execute_seq_scan(self, plan: ExecutionPlan) -> List[Record]:
        """执行SeqScan计划"""
        table_name = plan.attributes['table_name']
        return self.storage_engine.scan_records(table_name)
    
    def execute_filter(self, plan: ExecutionPlan) -> List[Record]:
        """执行Filter计划"""
        condition = plan.attributes['condition']
        
        # 执行子计划
        child_plan = plan.children[0] if plan.children else None
        if not child_plan:
            return []
        
        records = self.execute_plan(child_plan)
        if not isinstance(records, list):
            return []
        
        # 应用过滤条件
        filtered_records = []
        for record in records:
            if self._evaluate_condition(condition, record.data, record.schema):
                filtered_records.append(record)
        
        return filtered_records
    
    def execute_project(self, plan: ExecutionPlan) -> List[Dict]:
        """执行Project计划"""
        columns = plan.attributes['columns']
        
        # 执行子计划
        child_plan = plan.children[0] if plan.children else None
        if not child_plan:
            return []
        
        records = self.execute_plan(child_plan)
        if not isinstance(records, list):
            return []
        
        # 投影列
        result = []
        for record in records:
            if columns == '*':
                result.append(record.data)
            else:
                selected_data = {}
                for col in columns:
                    if col in record.data:
                        selected_data[col] = record.data[col]
                result.append(selected_data)
        
        return result
    
    def execute_update(self, plan: ExecutionPlan) -> str:
        """执行Update计划"""
        table_name = plan.attributes['table_name']
        assignments = plan.attributes['assignments']
        
        # 执行子计划获取要更新的记录
        child_plan = plan.children[0] if plan.children else None
        if not child_plan:
            return "没有记录需要更新"
        
        records = self.execute_plan(child_plan)
        if not isinstance(records, list):
            return "没有记录需要更新"
        
        updated_count = 0
        for record in records:
            # 构建新数据
            new_data = record.data.copy()
            for assignment in assignments:
                col_name = assignment['column']
                new_value = assignment['value']
                new_data[col_name] = new_value
            
            # 更新记录
            success = self.storage_engine.update_record(table_name, record.record_id, new_data)
            if success:
                updated_count += 1
        
        return f"成功更新 {updated_count} 条记录"
    
    def execute_delete(self, plan: ExecutionPlan) -> str:
        """执行Delete计划"""
        table_name = plan.attributes['table_name']
        
        # 执行子计划获取要删除的记录
        child_plan = plan.children[0] if plan.children else None
        if not child_plan:
            return "没有记录需要删除"
        
        records = self.execute_plan(child_plan)
        if not isinstance(records, list):
            return "没有记录需要删除"
        
        deleted_count = 0
        for record in records:
            # 删除记录
            success = self.storage_engine.delete_record(table_name, record.record_id)
            if success:
                deleted_count += 1
        
        return f"成功删除 {deleted_count} 条记录"
    
    def _evaluate_condition(self, condition: Dict, record_data: Dict, schema: Dict) -> bool:
        """评估WHERE条件"""
        if condition['type'] in ['EQ', 'NE', 'LT', 'LE', 'GT', 'GE']:
            left_value = self._evaluate_expression(condition['left'], record_data, schema)
            right_value = self._evaluate_expression(condition['right'], record_data, schema)
            
            if condition['type'] == 'EQ':
                return left_value == right_value
            elif condition['type'] == 'NE':
                return left_value != right_value
            elif condition['type'] == 'LT':
                return left_value < right_value
            elif condition['type'] == 'LE':
                return left_value <= right_value
            elif condition['type'] == 'GT':
                return left_value > right_value
            elif condition['type'] == 'GE':
                return left_value >= right_value
        
        return False
    
    def _evaluate_expression(self, expr: Any, record_data: Dict, schema: Dict) -> Any:
        """评估表达式"""
        if isinstance(expr, str):
            # 列引用
            if expr in record_data:
                return record_data[expr]
            else:
                # 可能是字面量
                try:
                    return int(expr)
                except ValueError:
                    try:
                        return float(expr)
                    except ValueError:
                        return expr
        
        return expr

class StorageEngine:
    """存储引擎"""
    
    def __init__(self, data_dir: str = "data"):
        """初始化存储引擎"""
        self.storage_system = PageStorageSystem(data_dir)
        self.cache_manager = CacheManager(buffer_size=100)
        self.cache_manager.set_storage_system(self.storage_system)
        self.table_manager = TablePageManager(self.storage_system)
        self.catalog = SystemCatalog(self.storage_system, self.table_manager)
    
    def create_table(self, table_name: str, schema: Dict) -> bool:
        """创建表"""
        return self.catalog.create_table(table_name, schema)
    
    def drop_table(self, table_name: str) -> bool:
        """删除表"""
        return self.catalog.drop_table(table_name)
    
    def get_table_schema(self, table_name: str) -> Optional[Dict]:
        """获取表结构"""
        return self.catalog.get_table_schema(table_name)
    
    def list_tables(self) -> List[str]:
        """列出所有表"""
        return self.catalog.list_tables()
    
    def insert_record(self, table_name: str, record: Record) -> bool:
        """插入记录"""
        schema = self.get_table_schema(table_name)
        if not schema:
            return False
        
        # 序列化记录
        record_bytes = record.serialize()
        record_size = len(record_bytes)
        
        # 查找有足够空间的页面
        pages = self.catalog.get_table_pages(table_name)
        target_page_id = None
        
        for page_id in pages:
            page_data = self.cache_manager.get_page(page_id)
            if page_data:
                free_space = self._get_page_free_space(page_data)
                if free_space >= record_size:
                    target_page_id = page_id
                    break
        
        if target_page_id is None:
            # 分配新页面
            target_page_id = self.catalog.add_page_to_table(table_name)
            if target_page_id == -1:
                return False
        
        # 在页面中插入记录
        page_data = self.cache_manager.get_page(target_page_id)
        if page_data:
            success = self._insert_record_in_page(page_data, record_bytes, record)
            if success:
                self.cache_manager.mark_dirty(target_page_id)
                return True
        
        return False
    
    def scan_records(self, table_name: str) -> List[Record]:
        """扫描所有记录"""
        schema = self.get_table_schema(table_name)
        if not schema:
            return []
        
        records = []
        pages = self.catalog.get_table_pages(table_name)
        
        for page_id in pages:
            page_data = self.cache_manager.get_page(page_id)
            if page_data:
                page_records = self._scan_page_records(page_data, schema, page_id)
                records.extend(page_records)
        
        return records
    
    def update_record(self, table_name: str, record_id: int, new_data: Dict) -> bool:
        """更新记录"""
        schema = self.get_table_schema(table_name)
        if not schema:
            return False
        
        # 简化实现：重新插入记录
        new_record = Record(new_data, schema)
        return self.insert_record(table_name, new_record)
    
    def delete_record(self, table_name: str, record_id: int) -> bool:
        """删除记录"""
        # 简化实现：标记删除
        return True
    
    def _get_page_free_space(self, page_data: bytearray) -> int:
        """获取页面空闲空间"""
        return struct.unpack('<I', page_data[4:8])[0]
    
    def _set_page_free_space(self, page_data: bytearray, free_space: int):
        """设置页面空闲空间"""
        struct.pack_into('<I', page_data, 4, free_space)
    
    def _get_page_record_count(self, page_data: bytearray) -> int:
        """获取页面记录数"""
        return struct.unpack('<I', page_data[0:4])[0]
    
    def _set_page_record_count(self, page_data: bytearray, count: int):
        """设置页面记录数"""
        struct.pack_into('<I', page_data, 0, count)
    
    def _insert_record_in_page(self, page_data: bytearray, record_bytes: bytearray, record: Record) -> bool:
        """在页面中插入记录"""
        free_space = self._get_page_free_space(page_data)
        record_size = len(record_bytes)
        
        if free_space < record_size:
            return False
        
        # 找到插入位置（页面末尾）
        record_count = self._get_page_record_count(page_data)
        insert_pos = 4096 - free_space
        
        # 插入记录
        page_data[insert_pos:insert_pos + record_size] = record_bytes
        
        # 更新页面头部
        self._set_page_record_count(page_data, record_count + 1)
        self._set_page_free_space(page_data, free_space - record_size)
        
        # 设置记录ID
        record.record_id = insert_pos
        
        return True
    
    def _scan_page_records(self, page_data: bytearray, schema: Dict, page_id: int) -> List[Record]:
        """扫描页面中的记录"""
        records = []
        record_count = self._get_page_record_count(page_data)
        
        if record_count == 0:
            return records
        
        # 简化实现：假设记录是连续存储的
        pos = 8  # 跳过页面头部
        
        for i in range(record_count):
            if pos >= len(page_data):
                break
            
            try:
                record, new_pos = Record.deserialize(page_data, schema, pos)
                record.record_id = pos
                records.append(record)
                pos = new_pos
            except Exception as e:
                print(f"反序列化记录失败: {e}")
                pos += 4
                continue
        
        return records

class DatabaseEngineV2:
    """数据库引擎 V2.0"""
    
    def __init__(self, data_dir: str = "data"):
        """初始化数据库引擎"""
        self.storage_engine = StorageEngine(data_dir)
        self.execution_engine = ExecutionEngine(self.storage_engine)
    
    def execute_plan(self, plan: ExecutionPlan) -> Any:
        """执行执行计划"""
        return self.execution_engine.execute_plan(plan)
    
    def get_database_info(self) -> Dict[str, Any]:
        """获取数据库信息"""
        cache_stats = self.storage_engine.cache_manager.get_cache_statistics()
        storage_info = self.storage_engine.storage_system.get_page_count()
        catalog_info = self.storage_engine.catalog.get_catalog_info()
        
        return {
            'tables': catalog_info['tables'],
            'table_count': catalog_info['table_count'],
            'cache_stats': cache_stats,
            'storage_pages': storage_info
        }
    
    def print_database_info(self):
        """打印数据库信息"""
        info = self.get_database_info()
        
        print("数据库引擎信息:")
        print(f"  表数量: {info['table_count']}")
        print(f"  存储页面数: {info['storage_pages']}")
        
        print("\n缓存统计:")
        cache_stats = info['cache_stats']
        print(f"  命中率: {cache_stats['hit_rate']:.2%}")
        print(f"  命中次数: {cache_stats['hit_count']}")
        print(f"  未命中次数: {cache_stats['miss_count']}")
        print(f"  已使用帧数: {cache_stats['used_frames']}/{cache_stats['buffer_size']}")
        
        print("\n表信息:")
        self.storage_engine.catalog.print_catalog()
    
    def shutdown(self):
        """关闭数据库引擎"""
        print("正在关闭数据库引擎...")
        self.storage_engine.cache_manager.flush_all()
        print("数据库引擎已关闭")

# 测试函数
if __name__ == "__main__":
    engine = DatabaseEngineV2("test_engine")
    
    try:
        # 测试创建表
        print("测试创建表...")
        schema = {
            'columns': {
                'id': {'type': {'type': 'int'}, 'primary_key': True},
                'name': {'type': {'type': 'varchar', 'length': 50}},
                'age': {'type': {'type': 'int'}}
            },
            'primary_key': 'id'
        }
        
        success = engine.storage_engine.create_table("users", schema)
        print(f"创建表: {'成功' if success else '失败'}")
        
        # 测试插入记录
        print("\n测试插入记录...")
        record = Record({'id': 1, 'name': 'Alice', 'age': 25}, schema)
        success = engine.storage_engine.insert_record("users", record)
        print(f"插入记录: {'成功' if success else '失败'}")
        
        # 测试扫描记录
        print("\n测试扫描记录...")
        records = engine.storage_engine.scan_records("users")
        print(f"扫描到 {len(records)} 条记录")
        for record in records:
            print(f"  {record.data}")
        
        # 打印数据库信息
        print("\n数据库信息:")
        engine.print_database_info()
        
    finally:
        engine.shutdown()
